-
Notifications
You must be signed in to change notification settings - Fork 133
Add react-native-sdk package #2745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces a new @onflow/react-native-sdk package that brings Flow blockchain integration to React Native and Expo applications. The package provides the same hooks as react-sdk plus native mobile components (Connect and Profile) for wallet management. Additionally, it includes improvements to the WalletConnect integration in fcl-react-native for better mobile deep linking support and disconnect handling.
Key changes:
- New React Native SDK package with hooks, components, and providers
- Enhanced WalletConnect service with deep link transformation for mobile wallets
- Refactored disconnect logic to properly clean up WalletConnect sessions
- Updated demo with mobile starter template promotion
Reviewed changes
Copilot reviewed 26 out of 27 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/react-native-sdk/package.json | Package manifest for the new React Native SDK |
| packages/react-native-sdk/src/index.ts | Main entry point re-exporting hooks, components, and types |
| packages/react-native-sdk/src/provider/FlowProvider.tsx | Provider component that sets up Flow client and context |
| packages/react-native-sdk/src/provider/GlobalTransactionProvider.tsx | Provider for tracking global transaction state |
| packages/react-native-sdk/src/components/Connect.tsx | Wallet connection button with profile modal |
| packages/react-native-sdk/src/components/Profile.tsx | User profile component displaying balance and wallet info |
| packages/react-native-sdk/src/icons/*.tsx | Icon components for UI elements |
| packages/fcl-react-native/src/walletconnect/service.ts | Enhanced deep link transformation for mobile wallets |
| packages/fcl-react-native/src/walletconnect/client.ts | New disconnect utility function |
| packages/fcl-react-native/src/fcl-react-native.ts | Refactored unauthenticate to use shared disconnect logic |
| packages/fcl-react-native/src/client.ts | Updated createFlowClient with disconnect integration |
| packages/demo/src/components/starter-banner.tsx | Added mobile starter template card |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } & ( | ||
| | {vaultIdentifier: string; erc20Address?: never} | ||
| | {vaultIdentifier?: never; erc20Address: string} | ||
| ) |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The TokenConfig type doesn't allow both vaultIdentifier and erc20Address to be specified simultaneously, which prevents supporting tokens that exist on both Cadence and EVM. Consider allowing both properties to be optional without mutual exclusion to support cross-VM tokens.
| } & ( | |
| | {vaultIdentifier: string; erc20Address?: never} | |
| | {vaultIdentifier?: never; erc20Address: string} | |
| ) | |
| vaultIdentifier?: string | |
| erc20Address?: string | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct as it is because you must specify one or the other like in react-sdk
| // Transform uid format (e.g., "frw#authz") to deeplink URL (e.g., "frw://authz") | ||
| // Service UIDs use # as separator but mobile deeplinks need :// | ||
| if (uid && uid.includes("#") && !uid.includes("://")) { | ||
| return uid.replace("#", "://") |
Copilot
AI
Dec 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string replacement logic assumes UIDs always have exactly one '#' character. If a UID contains multiple '#' characters, only the first will be replaced, potentially creating an invalid deep link. Consider using a more robust transformation that handles edge cases or validates the UID format.
| return uid.replace("#", "://") | |
| const [scheme, ...rest] = uid.split("#"); | |
| return scheme + "://" + rest.join("#"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed because in case it has multiple "#" it's a wrong uid and it should error
| @@ -0,0 +1,59 @@ | |||
| # @onflow/react-native-sdk | |||
|
|
|||
| A React Native library that provides hooks for interacting with the Flow blockchain. It helps you authenticate users, run Cadence scripts and transactions, listen to events, and manage network configuration directly from your components. Fully compatible with Expo. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a line here about it being comparable to the React SDK and add a link. We could maybe add a link to the hooks section of the playground as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
| </FlowProvider> | ||
| ``` | ||
|
|
||
| ## 🪝 Available Hooks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put a link to a full list in case it gets out of date. And also put a note like here is a look at some of the hooks available just to account for the list becoming not up to date.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
| | {vaultIdentifier?: never; erc20Address: string} | ||
| ) | ||
|
|
||
| export interface ConnectProps { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these the same as the connect component? Should it be shared? How do we make sure these don't get out of sync. Or maybe it doesn't matter if they do since they are separate components?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep for the components i'd keep them separated because they could have different implementations, like in this case where there is a reduced interface.
| ) | ||
| } | ||
|
|
||
| const styles = StyleSheet.create({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the component themeable like React SDK? I don't think we need to bother with that, but on the docs/playground we should make a note that theming is not available in the React Native SDK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's not themeable right now for sake of simplicity (i saw we were doing some changes also on react-sdk so i avoided doing that). Yep, i omitted the theme section in the Docs (PR).
| minHeight: 48, | ||
| }, | ||
| disconnectedButton: { | ||
| backgroundColor: "#0F172A", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make a file to share the colors across components? Same with any other styling constants.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, added!
| if (!chainId) return [] | ||
|
|
||
| const getFlowTokenAddress = () => { | ||
| if (chainId === "emulator" || chainId === "local") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have constants for these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you that constants would be cleaner, but useFlowChainId return value is a string and probably it's ok and flexible enough with the forking changes we are doing where the chainId can be mainnet-fork, etc
| const address = getFlowTokenAddress().replace("0x", "") | ||
| return [ | ||
| { | ||
| symbol: "FLOW", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Constant for this as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should be constant? It's implemented exactly as the Profile component in the react-sdk and it's already getting the address from contracts constants. If it's the name or symbol hopefully it won't ever change 😂
| : CONTRACT_ADDRESSES.mainnet.FlowToken | ||
| } | ||
|
|
||
| const address = getFlowTokenAddress().replace("0x", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use the withPrefix method we have
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep updated with correct method also on react-sdk profile component for consistency
|
|
||
| const displayAddress = useMemo(() => { | ||
| if (!user?.addr) return "" | ||
| return `${user.addr.slice(0, 6)}...${user.addr.slice(-4)}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this to a helper? I feel like we must do this often
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes make sense. I've created a util function "truncateAddress" in react-core and updated usage in both react-sdk and react-native-sdk using it
| }, | ||
| } | ||
|
|
||
| export function FlowProvider({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How similar is this to the provider we already have? Can't be shared?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The providers (react-sdk and react-native-sdk) share interfaces and contexts from react-core (FlowConfig, FlowConfigContext, FlowClientContext, QueryClient setup) but the implementation differs due to platform requirements:
- react-sdk: Uses fcl and web-specific ConnectModal
- react-native-sdk: Uses fcl-react-native and mobile-specific ConnectModalProvider
The core logic (config context, client creation, query client setup) is identical, the difference is the fcl import and modal provider which are platform specific, so for this their implementation is different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you might be able to just wrap this at a platform-level @mfbz
i.e.
function Foo() {
const flowClient = //platform-specific impl
return (<>
<FlowProvider flowClient={flowClient}>
</)
}
(or similar)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the fact was that the FlowProvider for react-native-sdk still needed some platform-specific implementation, like the ConnectModalProvider to show the connect modal and some other minor things that were needed in order to make it have the same usage as the react-sdk provider. For this reason, even if i created a FlowProviderCore wrapper it would have inside only a few things and it would still need a platform specific implementation so i kept them with the same interface but separated.
| import {QueryClient} from "@tanstack/react-query" | ||
| import {FlowQueryClientContext} from "@onflow/react-core" | ||
|
|
||
| export function FlowQueryClientProvider({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same question
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case they are basically the same. I have moved it to react-core so that it's reused for both react-sdk and react-native-sdk, thanks for the catch!
Added react-native-sdk package, similar to react-sdk but for react-native applications. It fully supports all the same hooks available in react-sdk, plus the connect and profile components. It leverages fcl-react-native for managing blockchain interactions and it's compatible to both react-native and expo applications.
Here's some screenshots: